#!/usr/bin/bash

EXCLUDEFILE="/etc/nmtrust/excluded_networks"
TRUSTFILE="/etc/nmtrust/trusted_networks"

###############################################################################

# Use colors, but only if connected to a terminal, and that terminal supports them.
if [ -n "$TERM" ] && which tput >/dev/null 2>&1; then
    ncolors=$(tput colors)
fi
if [ -t 1 ] && [ -n "$ncolors" ] && [ "$ncolors" -ge 8 ]; then
    RED="$(tput setaf 1)"
    GREEN="$(tput setaf 2)"
    YELLOW="$(tput setaf 3)"
    NORMAL="$(tput sgr0)"
else
    RED=""
    GREEN=""
    YELLOW=""
    NORMAL=""
fi

usage() {
    echo "Usage: nmtrust [OPTION...]
Determine if the current NetworkManager connections are trusted.

Options:
    -e      specify an alternative location for the excluded networks file
    -t      specify an alternative location for the trusted networks file
    -q      be quiet
    -v      be verbose"
}

message() {
    if [ "$quiet" != true ]; then
        echo "$@"
    fi
}

file_check() {
    excludefile_dir=$(dirname "$EXCLUDEFILE")
    if [ ! -d "$excludefile_dir" ]; then
        if mkdir -p "$excludefile_dir"; then
            message "Created configuration directory: $excludefile_dir"
        else
            message "Failed to create configuration directory: $excludefile_dir"
            exit 1
        fi
    fi
    if [ ! -f "$EXCLUDEFILE" ]; then
        if touch $EXCLUDEFILE; then
            message "Created empty excluded networks file: $EXCLUDEFILE"
        else
            message "Failed to create excluded networks file: $EXCLUDEFILE"
            exit 1
        fi
    fi
    if [ ! -f "$TRUSTFILE" ]; then
        message "Could not locate trusted networks file: $TRUSTFILE"
        exit 1
    fi
}

check_connection() {
    local name=$1
    local connection_excluded=false
    mapfile -t excludes < <(grep -v '^#' < $EXCLUDEFILE)
    for exclude in "${excludes[@]}"; do
        # NOTE: Cannot quote right-hand site of == because glob matching is needed [shellcheck(SC2053)]
        if [[ "$name" == $exclude ]]; then
            connection_excluded=true
            break
        fi
    done
    echo $connection_excluded
}

list_connections() {
    mapfile -t nmcli < <(nmcli conn show --active | grep -v loopback)

    for (( i=0; i<${#nmcli[@]}; i++ )); do
    if [ "$i" -eq 0 ]; then
        echo "${nmcli[$i]}STATUS"
    else
        name=$(echo "${connections[$i-1]}" | awk -F ":" '{print $1}')
        uuid=$(echo "${connections[$i-1]}" | awk -F ":" '{print $2}')
        if grep -q "$uuid" "$TRUSTFILE"; then
        echo "${GREEN}${nmcli[$i]}trusted${NORMAL}"
        elif [[ $(check_connection "$name") = true ]]; then
        echo "${GREEN}${nmcli[$i]}excluded${NORMAL}"
        else
        echo "${RED}${nmcli[$i]}untrusted${NORMAL}"
        fi
    fi
    done
}

trusted() {
    message "${GREEN}All connections are trusted${NORMAL}"
    if [ "$verbose" == true ]; then
        echo
        list_connections
    fi
    exit 0
}

all_untrusted() {
    message "${RED}All connections are untrusted${NORMAL}"
    if [ "$verbose" == true ]; then
        echo
        list_connections
    fi
    exit 2
}

untrusted() {
    message "${RED}${1-One or more} connections are untrusted${NORMAL}"
    if [ "$verbose" == true ]; then
        echo
        list_connections
    fi
    exit 3
}

no_network() {
    message "${YELLOW}There are no active connections${NORMAL}"
    exit 4
}

while getopts ":e:t:qvh" opt; do
    case $opt in
        e)
            EXCLUDEFILE=$OPTARG
            ;;
        t)
            TRUSTFILE=$OPTARG
            ;;
        q)
            quiet=true
            ;;
        v)
            verbose=true
            ;;
        :)
            echo "Option -$OPTARG requires an argument."
            usage
            exit 1
            ;;
        h | *)
            usage
            exit
            ;;
    esac
done

# Check if the excluded and trusted networks files exist.
file_check

# Get all active connections.
mapfile -t connections < <(nmcli --terse -f name,uuid,type conn show --active | grep -v loopback)

# Get number of active connections.
num_connections=0
for connection in "${connections[@]}"; do
    name=$(echo "$connection" | awk -F ":" '{print $1}')
    if [[ $(check_connection "$name") = false ]]; then
        ((num_connections++))
    fi
done

# Get number of trusted connections.
num_trusted=$(comm -12 <(nmcli --terse -f uuid,type conn show --active | grep -v loopback | sed -e 's/:.*$//' | sort) <(sort "$TRUSTFILE") | wc -l)

# Determine if there are active connections.
if [ "$num_connections" -eq 0 ]; then
    no_network
# Check if any of the active connections are untrusted.
elif [[ "$num_trusted" -eq 0 ]]; then
    all_untrusted
else
    for connection in "${connections[@]}"; do
        name=$(echo "$connection" | awk -F ":" '{print $1}')
        uuid=$(echo "$connection" | awk -F ":" '{print $2}')
        if [[ $(check_connection "$name") = false ]] && ! grep -q ^"$uuid"$ "$TRUSTFILE"; then
            num_untrusted=$((num_connections - num_trusted))
            untrusted "$num_untrusted of $num_connections"
        fi
    done
fi
# If we're still here, all connections are trusted.
trusted
